{-
    Roll.hs: Provides random die rolling functions.
    ----------------------------------------------------------------------------
    Version: 1.0alpha1
    Author:  Christopher Granade (cgranade@gmail.com)
    ----------------------------------------------------------------------------
    This file is a part of the Die Rolling Analysis Toolkit (DRAT) project.
    http://drat.googlecode.com/
    ----------------------------------------------------------------------------
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 or 3 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    ----------------------------------------------------------------------------
    (c) 2007 Christopher E. Granade
-}
module Roll (
    -- | Rolls n s-sided dice trials times.
    monteCarlo,
    -- | Converts a single roll to a string.
    rollToString,
    -- | Rolls n s-sided dice.
    d,
    
    describeRolls, histOfRolls,
    
    sumBest
) where

    import Random
    import Monad
    import IO
    
    import Statistics
    import SetOps
    import List
    
    -- Declare type synonyms
    type ScoringMethod a = [a] -> a
    
    monteCarlo :: Int -> Int -> Int -> IO [[Int]]
    monteCarlo n s trials = sequence (map (d n) (take trials (repeat s)))
    
    rollToString      :: [Int] -> String
    rollToString roll =  show roll
        
    d     :: Int -> Int -> IO [Int]
    d n s =  liftM (d' n s) newStdGen
    
    d'       :: RandomGen a => Int -> Int -> a -> [Int]
    d' n s g =  take n (randomRs (1::Int, s) g)
    
    allRolls :: [Int] -> [[Int]]
    allRolls (die:dice)
    	| n == 0 = [[1..die]] 
    	| n == 1 = crossToList [1..die] [1..head(dice)]
    	| n >= 2 = crossToListRep [1..die] (allRolls dice)
   	  where n = length dice
    
    describeRolls    :: (RealFloat b, Integral a) => ScoringMethod a -> [[a]] -> ((b, b, b), b, b, b)
    describeRolls method rs =  describe (map fromIntegral (map method rs))

    bestNRolls :: (Integral a) => Int -> Int -> a -> [a]
    bestNRolls t n s = map sum (map (bestNOf t) (selfcross [1..s] n))
    
    bestNOf       :: Ord a => Int -> [a] -> [a]
    bestNOf n xs  =  take n ((reverse . sort) xs)

    sumBest      :: (Num a, Ord a) => Int -> ScoringMethod a
    sumBest n xs =  sum (bestNOf n xs)
    
    histOfRolls              :: (Ord a, Fractional b) => ScoringMethod a -> [[a]] -> [b]
    histOfRolls method rolls =  histNorm (map method rolls)